﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using FlanProjectData.Characters;
using FlanProjectData.GameObjects;
using FlanProjectData.Helpers;
using FlanProjectData.Managers;
using FlanProjectData.Sprites;

namespace FlanProjectData.Maps
{
    public class Map
    {
        #region Properties

        #region Debug

        private static Logger logger = new Logger("Map");
        private List<NonPlayableCharacter> npc = new List<NonPlayableCharacter>();
		public List<String> ListNpcToLoad { get; set; }
        private Quest q = new Quest();
        public event EventHandler<Event> evt;

        #endregion

        #region Directions

        protected const int MOVE_UP = -1;
        protected const int MOVE_DOWN = 1;
        protected const int MOVE_LEFT = -1;
        protected const int MOVE_RIGHT = 1;

        #endregion

        #region Map Data

        private int collisionValue = 0;

        public String Name { get; set; }
        public Point MapSize { get; set; }

        #endregion

        #region Map Layers

        public String CollisionLayer { get; set; }
        public String MapLayer { get; set; }
        public String ItemLayer { get; set; }

        [ContentSerializerIgnore]
        public List<Tile> TileList { get; set; }
        [ContentSerializerIgnore]
        public int[,] MapCollisionLayer { get; set; }
        [ContentSerializerIgnore]
        public int[,] MapMapLayer { get; set; }
        [ContentSerializerIgnore]
        public int[,] MapItemLayer { get; set; }

        #endregion

        #region Events

        public String EventsLayer { get; set; }
        public String EventsFile { get; set; }

        [ContentSerializerIgnore]
        public List<Event> EventsList { get; set; }

        [ContentSerializerIgnore]
        public int[,] MapEventsLayer { get; set; }

        #endregion

        #region Combat Events

        private const int lValueProb = 0;
        private const int tValueProb = 100;
        private const int probability = 1;
        private const int tileSize = 32;
        private int cptMvtFromLastBattle = 0;
        private Vector2 dep = Vector2.Zero;

        private bool fightEvent = false;
        public event EventHandler Fighting;

        [ContentSerializerIgnore]
        public bool FightEvent
        {
            get { return fightEvent; }
            set
            {
                bool fireEvent = !fightEvent && value;
                fightEvent = value;
                if (fireEvent && (Fighting != null))
                {
                    Fighting(this, EventArgs.Empty);
                }
            }
        }

        #endregion

        #region Party

        // Character on the screen
        private AnimatingSprite currentPartyAnimation = new AnimatingSprite();
        private bool hasMoved = false;

        public enum States { MoveUp, MoveDown, MoveLeft, MoveRight, Standby }
        [ContentSerializerIgnore]
        public States CurrentState { get; set; }

        [ContentSerializerIgnore]
        public Party Team { get; set; }

        // Set the position of the map can be draw
        [ContentSerializerIgnore]
        public Vector2 Position { get; set; }
        [ContentSerializerIgnore]
        public Vector2 Direction { get; set; }
        [ContentSerializerIgnore]
        public Vector2 ScreenCenter { get; set; }
        [ContentSerializerIgnore]
        public bool LootItem { get; set; }
        [ContentSerializerIgnore]
        public Inventory Loot { get; set; }

        #endregion

        #region Saves

        private string nameSave;

        #endregion

        #endregion

        public Map()
        {
            this.LootItem = false;
        }

        public void LoadContent(ContentManager contentManager)
        {
            try
            {
                this.Loadingloot(contentManager);

                // Size map layer
                MapCollisionLayer = new int[MapSize.X, MapSize.Y];
                MapMapLayer = new int[MapSize.X, MapSize.Y];
                MapItemLayer = new int[MapSize.X, MapSize.Y];
                MapEventsLayer = new int[MapSize.X, MapSize.Y];

                // Load map Tiles
                TileList = contentManager.Load<List<Tile>>("Data/Maps/SpritePlace");

                // Chargement des Textures2D
                foreach (var tile in TileList)
                {
                    tile.SpriteTexture = contentManager.Load<Texture2D>(tile.SpriteTextureAsset);
                }

                // Get Content to different map layers
                SetMapContent(MapLayer, MapMapLayer);
                SetMapContent(ItemLayer, MapItemLayer);
                SetMapContent(CollisionLayer, MapCollisionLayer);
                SetMapContent(EventsLayer, MapEventsLayer);

                this.Team = contentManager.Load<Party>(@"Data\Party\Party");

                //Load the saved game
                this.LoadingParty();
                this.Team.LoadContent(contentManager);
                this.currentPartyAnimation.Animation = this.Team.Standby;
                this.currentPartyAnimation.FrameIndex = 0;

                // Load Events File
                EventsList = contentManager.Load<List<Event>>("Data/Maps/" + EventsFile);

				// Load PNJ on map
				foreach (string s in ListNpcToLoad)
				{
					this.npc.Add(contentManager.Load<NonPlayableCharacter>("Data/Characters/NonPlayableCharacters/" + s));
				}
            }
            catch (Exception e)
            {
                logger.Error(e.Message);
            }
        }

        public void Update()
        {
            hasMoved = false;
            dep = Vector2.Zero;

            HandleInput();

            if (hasMoved)
            {
                UpdateAnimation();

                if (RandomizeNewCombat())
                    FightEvent = true;

                if (this.LootItem)
                {
                   
                    this.Team.addItemInventory(Loot.getRandomItem());
                    this.LootItem = false;
                    
                }

                // Position += Direction * Team.MovementSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
                this.Team.Position = this.Position;

                // hasMoved = false;
            }
            else
            {
                int eventHere = MapEventsLayer[(int)(this.Position.X + dep.X + ScreenCenter.X), (int)(this.Position.Y + dep.Y + ScreenCenter.Y)];
				
                if (eventHere > 0)
                {
                    Console.WriteLine("Event launched :" + eventHere);
                    // Gestion des evenements
                    
					NonPlayableCharacter n = ThereIsNPCHere(eventHere);
                    if (n != null)
                    {
                        logger.Debug("I'll try to contact NPC : " + n.IdEvent);
                        // Si le PNJ peut fournir une quete au joueur, il la lui donne
                        if (this.Team.currentQuest == null && n.canIGiveAQuest(eventHere))
                        {
                            Quest newQuest = n.getQuest();
							if (!this.Team.ThisQuestIsEverDone(newQuest.IdQuest) && (newQuest.IdQuest == Team.NextQuest))
							{
								// No quest is running for the moment
								logger.Debug("New Quest is given");
								this.Team.currentQuest = newQuest;
								GenerateEvent(n, new Event { msg = this.Team.currentQuest.GetQuestInfomation() });
								this.Team.currentQuest.nextStep();
							}
							else
							{
								// Case : Player has done this quest before
								logger.Debug("[" + newQuest.QuestName + "] cannot be done");
								n.IndexQuest++;
							}
                        }
                        else
                        {
                            if (this.Team.currentQuest != null)
                            {
                                // Check if the event throw is the next step
                                if (this.Team.currentQuest.isCollision(eventHere))
                                {
                                    GenerateEvent(n, new Event { msg = this.Team.currentQuest.GetQuestInfomation() });
                                    if (this.Team.currentQuest.EndOfQuest())
                                    {
                                        logger.Debug("End of quest");
                                        this.Team.questDone.Add(this.Team.currentQuest);
										this.Team.NextQuest = this.Team.currentQuest.IdNextQuest;
                                        this.Team.currentQuest = null;
                                    }
                                    else
                                    {
                                        logger.Debug("Quest continue");
                                        this.Team.currentQuest.nextStep();
                                    }
                                }
                            }
                            else
                            {
                                logger.Info("Nothing to be done");
                            }
                        }
                    }
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            try
            {
                for (int j = 0; j < MapSize.Y; j++)
                {
                    for (int i = 0; i < MapSize.X; i++)
                    {
                        int idCase = MapMapLayer[i, j];
                        TileList[idCase].Draw(spriteBatch, TileList[idCase].SpriteTexture, new Vector2((i * tileSize) - (Position.X * tileSize), (j * tileSize) - (Position.Y * tileSize)));

                        int idCaseItem = MapItemLayer[i, j];
                        TileList[idCaseItem].Draw(spriteBatch, TileList[idCaseItem].SpriteTexture, new Vector2((i * tileSize) - (Position.X * tileSize), (j * tileSize) - (Position.Y * tileSize)));
						
                        /*int idCaseEvt = MapEventsLayer[i, j];
                        TileList[idCaseEvt].Draw(spriteBatch, TileList[idCaseEvt].SpriteTexture, new Vector2((i * tileSize) - (Position.X * tileSize), (j * tileSize) - (Position.Y * tileSize))); 
                         */
                    }
                }

                this.currentPartyAnimation.Draw(spriteBatch, SpriteEffects.None,
                    new Vector2(ScreenCenter.X * tileSize, ScreenCenter.Y * tileSize), gameTime);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public void UpdateAnimation()
        {
            if (currentPartyAnimation == null)
                throw new NullReferenceException("No animation loaded.");

            switch (CurrentState)
            {
                case States.MoveUp:
                    currentPartyAnimation.PlayAnimation(Team.MoveUp);
                    break;

                case States.MoveDown:
                    currentPartyAnimation.PlayAnimation(Team.MoveDown);
                    break;

                case States.MoveLeft:
                    currentPartyAnimation.PlayAnimation(Team.MoveLeft);
                    break;

                case States.MoveRight:
                    currentPartyAnimation.PlayAnimation(Team.MoveRight);
                    break;

                case States.Standby:
                    currentPartyAnimation.PlayAnimation(Team.Standby);
                    break;
            }

            if (currentPartyAnimation.IsAnimationComplete)
                currentPartyAnimation.Reset();
        }

        public void HandleInput()
        {
            if (!this.FightEvent)
            {
                if (InputManager.IsActionPressed(InputManager.Action.Up))
                {
                    CurrentState = States.MoveUp;

                    if (Position.Y > 0)
                    {
                        dep.Y = -1;
                        collisionValue = MapCollisionLayer[(int)(ScreenCenter.X + Position.X), (int)(ScreenCenter.Y + Position.Y) - 1];

                        if (collisionValue == 0)
                        {
                            Position = new Vector2(Position.X, Position.Y - 1);
                            hasMoved = true;
                        }
                    }
                }

                if (InputManager.IsActionPressed(InputManager.Action.Down))
                {
                    CurrentState = States.MoveDown;

                    if (this.Position.Y < (MapSize.Y - 22))
                    {
                        dep.Y = 1;
                        collisionValue = MapCollisionLayer[(int)(ScreenCenter.X + Position.X), (int)(ScreenCenter.Y + Position.Y) + 1];

                        if (collisionValue == 0)
                        {
                            Position = new Vector2(Position.X, Position.Y + 1);
                            hasMoved = true;
                        }
                    }
                }

                if (InputManager.IsActionPressed(InputManager.Action.Left))
                {
                    CurrentState = States.MoveLeft;

                    if (this.Position.X > 0)
                    {
                        dep.X = -1;
                        collisionValue = MapCollisionLayer[(int)(ScreenCenter.X + Position.X) - 1, (int)(ScreenCenter.Y + Position.Y)];

                        if (collisionValue == 0)
                        {
                            Position = new Vector2(Position.X - 1, Position.Y);
                            hasMoved = true;
                        }
                    }
                }

                if (InputManager.IsActionPressed(InputManager.Action.Right))
                {
                    CurrentState = States.MoveRight;

                    if (this.Position.X < (MapSize.X - 28))
                    {
                        dep.X = 1;
                        collisionValue = MapCollisionLayer[(int)(ScreenCenter.X + Position.X) + 1, (int)(ScreenCenter.Y + Position.Y)];

                        if (collisionValue == 0)
                        {
                            Position = new Vector2(Position.X + 1, Position.Y);
                            hasMoved = true;
                        }
                    }
                }

                if (InputManager.IsActionPressed(InputManager.Action.Save))
                {
                    Team.save(nameSave);
                }


                
            }
            if (InputManager.IsKeyTriggered(Keys.Z))
            {
                this.Team.addItemInventory(Loot.getRandomItem());
            }
        }

        public void SetMapContent(String mapFrom, int[,] mapTo)
        {
            String[] lines = mapFrom.Split(new char[] { '\n' });
            for (int i = 1; i < lines.Length - 1; i++)
            {
                String[] col = lines[i].Split(new char[] { ' ' });
                for (int j = 6; j < col.Length - 1; j++)
                {
                    mapTo[j - 6, i - 1] = Convert.ToInt32(col[j]);
                }
            }
        }

        public void AddSaveName(string save)
        {
            this.nameSave = save;
        }

        public void LoadingParty()
        {
            var xs = new XmlSerializer(typeof(Party));
            var savedParty = new Party();

            using (StreamReader rd = new StreamReader(@"Content/Saves/" + this.nameSave + "/save.xnb"))
            {
                savedParty = xs.Deserialize(rd) as Party;

                this.Team.PlayerList = savedParty.PlayerList;
                this.Team.Position = savedParty.Position;
                this.Team.inventory = savedParty.inventory;
				this.Team.currentQuest = savedParty.currentQuest;
				this.Team.questDone = savedParty.questDone;
				this.Team.NextQuest = savedParty.NextQuest;
                this.Position = this.Team.Position;
                /*
                logger.Debug("Partie charge!");
                logger.Debug("position de la Team: " + this.Team.position.X + "/" + this.Team.position.Y);
                logger.Debug("position de la carte: " + this.Position.X + "/" + this.Position.Y);
                 * */
            }
        }

        public void Loadingloot(ContentManager cm)
        {
            Loot = new Inventory();
            Loot.lstItems = cm.Load<List<Item>>(@"Data\Items\ListeItem");
        }

        private bool RandomizeNewCombat()
        {
            var randValue = (new Random()).Next(lValueProb, tValueProb);
            var activation = false;

            if (randValue < probability)
            {
                activation = true;
                logger.Info("A fight is launched Value : " + randValue + "<" + probability);
                logger.Info("Previous fight was " + this.cptMvtFromLastBattle + " time before");
                this.cptMvtFromLastBattle = 0;
            }

            this.cptMvtFromLastBattle++;

            return activation;
        }

        private NonPlayableCharacter ThereIsNPCHere(int idEvent)
        {
            NonPlayableCharacter r = null;
            int i = 0;
            while (i < npc.Count && r == null)
            {
                if (npc.ElementAt(i).IdEvent == idEvent)
                {
                    r = npc.ElementAt(i);
                }
                i++;
            }
            return r;
        }

        private void GenerateEvent(object sender, EventArgs e)
        {
            if (evt != null && sender is NonPlayableCharacter && e is Event)
                evt((NonPlayableCharacter)sender, (Event)e);
        }
    }
}
